/*
   程序名称：bkbase.h
   程序内容：一些基本的宏、类型和函数的定义
   程序作者：姚杰
   完成日期：2012 年 07 月 01 日
*/

#ifndef USR_BKBASE_H_
#define USR_BKBASE_H_

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef __cplusplus
extern "C" {
#endif

#ifdef BK_LINUX
   #define BK_PS_STRING "ps -e -o uid=xxxxx -o pid=xxxxxxxx " \
                        "-o comm=xxxxxxxxxxxxxxxx -o args="
   #define Bkhtonl(x) BkSwapLong(x)
   #define Bkntohl(x) BkSwapLong(x)
   #define Bkhtons(x) BkSwapShort(x)
   #define Bkntohs(x) BkSwapShort(x)
#endif

#ifdef BK_HP
   #define BK_PS_STRING "export UNIX_STD=2003;ps -e -o uid=xxxxx " \
      "-o pid=xxxxxxxx -o comm=xxxxxxxxxxxxxxxx -o args="
   #define Bkhtonl(x) (x)
   #define Bkntohl(x) (x)
   #define Bkhtons(x) (x)
   #define Bkntohs(x) (x)
#endif

#ifdef BK_AIX
   #define BK_PS_STRING "ps -e -o uid=xxxxx -o pid=xxxxxxxx " \
                        "-o comm=xxxxxxxxxxxxxxxx -o arg="
   #define Bkhtonl(x) (x)
   #define Bkntohl(x) (x)
   #define Bkhtons(x) (x)
   #define Bkntohs(x) (x)
   #define __W_EXITCODE(x, y) (x << 8)
#endif

#ifdef BK_SOLARIS
   typedef long mtyp_t;
   #define BK_PS_STRING "ps -e -o pid=xxxxxxxx -o comm=xxxxxxxxxxxxxxxx " \
                        "-o args="
   extern long random(void);
   extern void srandom(unsigned);
   #define Bkhtonl(x) (x)
   #define Bkntohl(x) (x)
   #define Bkhtons(x) (x)
   #define Bkntohs(x) (x)
#endif

#ifdef BK_DEBUG
   extern void BkAssert(char *FileName, int LineNo);
   #define BK_ASSERT(f) \
      if( f ) \
         BkDoNothing(); \
      else \
         BkAssert(__FILE__, __LINE__)
#else
   #define BK_ASSERT(f) BkDoNothing()
#endif

#define  BK_CLEAR(x)               memset(&(x), 0, sizeof(x))
#define  BK_EMPTY_STRING           ""
#define  BK_TAB                    '\t'
#define  BK_EOS                    '\0'
#define  BK_NEWLINE                '\n'
#define  BK_NEWPAGE                0x10
#define  BK_TIME_INFINITE          -1
#define  BK_MAX_DBF_REC_SIZE       500

#define  BK_FIND_DIRECT_NEXT       0
#define  BK_FIND_DIRECT_PREVIOUS   1

#define  BK_EOTHER                 -1  /* 其他错误 */
#define  BK_ETIMEOUT               -2  /* 超时 */
#define  BK_EINTR                  -3  /* 被信号中断 */
#define  BK_E2BIG                  -4  /* 返回数据超过要求的最大长度 */
#define  BK_ENOMEM                 -5  /* 内存空间不足 */
#define  BK_EDEADLK                -6  /* 检测到死锁 */

#define  BK_E2A                     0  /* EBCDIC TO ASCII */
#define  BK_A2E                     1  /* ASCII TO EBCDIC */
#define  BK_NC                      2  /* DO NOT CONVERT  */

typedef unsigned char BK_UC;
typedef unsigned short BK_UINT16;
typedef unsigned int BK_UINT32;
typedef void (*BK_HANDLER)(int);

typedef struct list_node {      /* 抽象单向链表结点 */
   void *data;                  /* 数据 */
   struct list_node *next;      /* 指向下一结点的指针 */
} BK_LIST_NODE;

typedef struct {                /* 抽象单向链表 */
   int n;                       /* 数据项数 */
   int len;                     /* 数据项长度 */
   BK_LIST_NODE *head;          /* 首指针 */
   BK_LIST_NODE *tail;          /* 尾指针 */
} BK_LIST;

typedef struct {
   FILE *f;
   uid_t uid;
   pid_t pid;
   char name[33];
   char argv0[501];
   char argv1[501];
} BK_PROC_INFO;

typedef struct {
   FILE *f;
   char *buf;
   int RecSize;
} BK_DBF_INFO;

typedef struct {
   int total_blocks;       /* 文件系统总空间的大小，以 M 为单位 */
   int free_blocks;        /* 文件系统剩余空间的大小，以 M 为单位 */
} BK_FS_INFO;

typedef struct {
   int n;                  /* 域个数 */
   int DbfRecLen;          /* 纪录长度 */
   int StructLen;          /* 对应的 C 语言结构大小（在每个域类型都是 */
                           /* char 的情况下） */
   char name[100][21];     /* 域名称数组，最多允许 100 个域 */
   int len[100];           /* 域长度数组 */
   char type[100];         /* 域类型数组 */
} BK_DBF_FIELDS_INFO;

typedef struct BkStringList{
   char *s;
   int selected;
   struct BkStringList *next;
} BK_STRING_LIST;

typedef struct { /* 纪录数组类型 */
   size_t size;  /* 纪录长度 */
   int n;        /* 纪录笔数 */
   char *p;      /* 纪录开始地址 */
} BK_ARRAY;

typedef struct { /* 大小自动调整数组类型 */
   size_t size;  /* 纪录长度 */
   int n;        /* 纪录笔数 */
   int step;     /* 每次最少扩充记录数 */
   char *p;      /* 纪录开始地址 */
} BK_SMART_ARRAY;

extern char BkTmpDir[501];

extern void BkDoNothing(void);

/* 将短整形 x 的字节序反过来 */
extern BK_UINT16 BkSwapShort(BK_UINT16 x);

/* 将长整形 x 的字节序反过来 */
extern BK_UINT32 BkSwapLong(BK_UINT32 x);

/* 将字符串 s 尾部的空格全部替换为 '\0' */
extern void BkRightTrim(char *s);

/* 如果字符串 s 长度小于 len，将其用字符 c 补够长度 len
   如果 s 长度大于 len，将其截断
   总之，函数执行完毕，s 的长度一定变为 len */
extern void BkFillChar(char *s, char c, size_t len);

/* 如果字符串 s 长度小于 len，将其用字符 c 从左边补够长度 len
   如果 s 长度大于 len，将其截断
   总之，函数执行完毕，s 的长度一定变为 len */
extern void BkFillLeft(char *s, char c, size_t len);

/* 将字符串 s 中的大写字母全部转换成小写，其余字符保持不变 */
extern void BkToLower(char *s);

/* 将字符串 s 中的小写字母全部转换成大写，其余字符保持不变 */
extern void BkToUpper(char *s);

/* 如果 s 是 NULL 或者 s 是空字符串返回真，否则返回假 */
extern int BkIsEmptyString(const char *s);

/* 将长度为 HexLen 的内存区域 HexBuf 的内容用 16 进制表示，
   结果放在 AscBuf 中，每个字节变成两个字节，如 0xfe 变成
   'f' 和 'e' 两个字符，因此 AscBuf 的长度至少要 HexLen * 2 */
extern void BkHexToAsc(const void *HexBuf, char *AscBuf, size_t HexLen);

/* 将长度为 HexLen * 2 的 16 进制字符串 AscBuf 放在内存区域 HexBuf
   中，每两个字节变成一个字节，如 'f' 和 'e' 两个字符 变成 0xfe，
   AscBuf 中可以用大写字母也可以用小写字母，HexBuf 的长度至少要 HexLen */
extern void BkAscToHex(void *HexBuf, const char *AscBuf, size_t HexLen);

/* 返回一个随机数 */
extern long BkRandom(void);

/* 用当前时间设置随机数发生器的初值 */
extern void BkRandomize(void);

/* 将内存区域 p 中长度为 len 的内容用容易理解的格式写到文件 f 中去，
   f 必须是用可写方式打开的，f 可以直接用 stdout 或 stderr */
extern void BkMemDump(FILE *f, const void *p, size_t len);

/* 从文件 f 中读一行到字符串 s 中，最多读 MaxLen 个字符，若某一行长度
   超过 MaxLen，多出的部分将被丢弃。制表符 '\t' 将被转换成一个空格。
   若到文件尾返回 -1，否则返回读到的字符数。若文件最后一行没有换行符
   则该行内容将被丢弃。f 是已经打开的文件 */
extern int BkReadLine(FILE *f, char *s, int MaxLen);

/* 将字符串 s 中的第一个单词拷贝到字符串 w 中。如果分隔符包含空格则 s
   中的前导空格将被忽略。
   第一个单词是指从第一个非空格字符开始到第一个空格或分隔符之前的内容。
   如果第一个非空格字符就是分隔符则认为第一个单词就是由该分隔符构成。
   单词的长度不超过 MaxLen。分隔符可以有多个，由参数 separators 确定。
   函数返回前导空格个数和该单词长度之和。下面是 BkReadWord 的典型应用：

   int r;
   char *s = "   akmnb c: d";
   char w[4];

   for(r = 0;;){
      r += BkReadWord(s + r, w, " :", 3);
      if( BkIsEmptyString(w) )
         break;
      printf("%s\n", w);
   }

   上面程序的结果为：
   akm
   nb
   c
   :
   d
*/
extern int BkReadWord(const char *s, char *w,
   const char *separators, int MaxLen);

/* 开始浏览进程表，正常返回进程数，失败返回 -1 */
extern int BkProcInfoStartBrow(BK_PROC_INFO *p);

/* 读下一条进程纪录，正常返回 0，无下一条纪录返回 -1
   可以获得如下进程信息：
   p->pid       进程号
   p->name      程序名
   p->argv0     程序全路径名
   p->argv1     程序的第一个参数
*/
extern int BkProcInfoReadNext(BK_PROC_INFO *p);

/* 结束进程表的浏览，释放所占用的资源 */
extern void BkProcInfoEndBrow(BK_PROC_INFO *p);

/* 开始浏览 dbf 格式文件，FileName 指出该文件名称，
   正常返回文件中的纪录数（包括已经删除的纪录），失败返回 -1
   参数 p->buf 必须指向大小不小于该文件纪录大小的内存区域 */

extern int BkDbfStartBrow(const char *FileName, BK_DBF_INFO *p);
/* 读下一条 dbf 纪录，正常返回 0，无下一条纪录返回 -1
   读出的纪录将保存在 p->buf 所指向的内存区域 */
extern int BkDbfReadNext(BK_DBF_INFO *p);

/* 结束 dbf 文件的浏览，释放所占用的资源 */
extern void BkDbfEndBrow(BK_DBF_INFO *p);

/* 获得 FileName 所指出的 dbf 文件的结构 */
extern int BkGetDbfFieldsInfo(const char *FileName, BK_DBF_FIELDS_INFO *p);

/* 将数据从 dbf 格式转换成 C 语言的结构（必须所有域的类型均为 char）
   输入参数 p 指出 dbf 的结构，可以先由 BkGetDbfFieldsInfo 函数获得该结构 */
extern void BkCopyDbfrecToStruct(BK_DBF_FIELDS_INFO *p,
   char *RecP, char *StructP);

/* 将数据从 C 语言的结构转换成 dbf 格式（必须所有域的类型均为 char）
   输入参数 p 指出 dbf 的结构，可以先由 BkGetDbfFieldsInfo 函数获得该结构 */
extern void BkCopyStructToDbfrec(BK_DBF_FIELDS_INFO *p,
   char *RecP, char *StructP);

/* 统计字符串 s 的行数 */
extern int BkCountLine(const char *s);

/* 返回字符串列表的第 n 个字符串 */
extern char *BkTheNthString(BK_STRING_LIST *sl, int n);

/* 从字符串 s 的 x 列开始查找指定字符串 pattern。成功返回 0，x 改为找到的
   字符串的位置，len 为找到的字符串的长度；失败返回 1。参数 direction 指出
   查找的方向，可以是 BK_FIND_DIRECT_NEXT 或者 BK_FIND_DIRECT_PREVIOUS */
extern int BkFindStringInString(const char *s, int *x,
   const char *pattern, int direction, int *len);

/* 执行一个命令，将其输出存入文件 ResultFile。返回该命令的返回码 */
extern int BkExecCommand(const char *command,
   const char *ResultFile, char **envs);

/* 将字符串 s2 拷贝到 s1，最多拷贝 len 个字符，拷贝结果不自动加 '\0' */
extern void BkStrNCpy(char *s1, const char *s2, size_t len);

/* 取本地时间 */
extern void BkLocalTime(time_t *t, struct tm *tm);

/* 计算日期 */
extern void BkDateCompute(char *NewDate, const char *OldDate, int Days);

/* 取本地今天日期 */
extern void BkToday(char *s);

/* 取本地当前时间 */
extern void BkCurrTime(char *s);

/* 对内存区域 buf 用 TEA 算法进行加密。len 是该区域的长度，必须
   是 8 的整数倍 */
extern void BkTeaEncrypt(void *buf, const void *key, size_t len);

/* 对内存区域 buf 用 TEA 算法进行解密。len 是该区域的长度，必须
   是 8 的整数倍 */
extern void BkTeaDecrypt(void *buf, const void *key, size_t len);

/* 对字符串 StringIn 用 TEA 算法进行加密，结果转成 16 进制字符串
   保存于 StringOut */
extern void BkTeaEncryptString(const char *StringIn,
   char *StringOut, const void *k);

/* 对 16 进制字符串 StringIn 用 TEA 算法进行解密，结果保存于 StringOut */
extern void BkTeaDecryptString(const char *StringIn,
   char *StringOut, const void *k);

/* 对内存区域 buf 求校验和，结果保存于 sum。len 是该区域的长度，必须
   是 8 的整数倍 */
extern void BkSimpleCheck(const char *buf, char sum[8], size_t len);

extern void BkVm(char *mem, char **ext, int start);

/* 内存分配，成功返回 0，p 中是分配到的内存地址；失败返回 -1，p 被置为 NULL */
extern int BkMalloc(void *p, size_t size);

/* 旧风格的 malloc 函数，成功返回分配到的内存地址；失败返回 NULL */
extern void *BkMallocOldStyle(size_t size);

/* 改变已分配内存的大小，成功返回 0，p 中是新的内存地址；失败返回 -1，p 的内
   容不变 */
extern int BkRealloc(void *p, size_t size);

/* 旧风格的 realloc 函数，成功返回分配到的内存地址；失败返回 NULL */
extern void *BkReallocOldStyle(void *p, size_t size);

/* 释放已分配内存 */
extern void BkFree(void *p);

/* 检查是否有未释放的内存 */
extern void BkCheckLostMemory(void);

/* 在字符串 s 尾部追加 n 个字符 c */
extern void BkCatNChars(char *s, size_t n, char c);

/* 将金额转换成大写字符串 */
extern void BkCapAmt(double amt, char *s, int MaxLen);

/* 检查字符串 s 是否全部是由字符 c 组成 */
int BkStringIsAll(const char *s, char c);

/* 格式化字符串，格式可以是 nnnn/nn/nn nnnn-nn-nn nn.n 等等 */
extern void BkFormatString(const char *src, char *des,
   const char *fmt, int BlankWhenZero);

/* 格式化浮点数，格式可以是
   zzz, zzz, zzn.nn  正数，左边填满空格
   ***, ***, **n.nn  正数，左边填满 *
   $$$, $$$, $$n.nn  正数，左边填一个 $
   ---, ---, --n.nn  浮动符号位，正数为空格，负数为 -
   +++, +++, ++n.nn  浮动符号位，正数为 +，负数为 -
*/
extern void BkFormatDouble(double d, char *des,
   const char *fmt, int BlankWhenZero);

/* 字符串左对齐 */
extern void BkLeftAligned(char *s, size_t len);

/* 字符串右对齐 */
extern void BkRightAligned(char *s, size_t len);

/* 写出错信息 */
extern void BkWriteErrorLog(const char *FormatStr, ...);
extern void BkPrintError(const char *FormatStr, ...);

/* 设置出错日志文件名称 */
extern void BkSetLogFile(const char *s);

/* 获得文件系统信息 */
extern int BkGetFsInfo(const char *path, BK_FS_INFO *p);

/* 从 src 拷贝 len 个字节到 des 并且在 des 后面加上字符串结束标志 */
extern void BkCpyAndAddEos(char *des, const void *src, size_t len);

/* 按键值从排过序纪录数组查找一笔纪录，如有多笔找到第一笔。正常返回该纪录
   下标，找不到返回 -1 */
extern int BkSearchArrByKey(BK_ARRAY arr, const void *buf,
   int KeyOff, size_t KeyLen);

/* 按键值从排过序纪录数组读一笔纪录，如有多笔读出第一笔 */
extern int BkReadArrByKey(BK_ARRAY arr, void *buf,
   int KeyOff, size_t KeyLen);

/* 按键值改写排过序纪录数组的一笔纪录，如有多笔改写第一笔 */
extern int BkWriteArrByKey(BK_ARRAY arr, void *buf, int KeyOff, size_t KeyLen);
/* 数组排序，按从小到大顺序，整个纪录比较大小 */
extern void BkSortArr(BK_ARRAY arr);

/* 忽略某个信号 */
extern BK_HANDLER BkSignalIgnore(int signo);

/* 将一个字符串中所有的某种字符全部替换成某个字符 */
extern void BkChangeChar(char *s, const char *CharsToReplace, char c);

/* 堆排序，接口类似 qsort，每找到最小值立即用 OutputRec 输出 */
extern int BkHsort(void *base, unsigned long n, unsigned long size,
   int (*f)(const void*, const void*), void (*OutputRec)(void*));

/* 2 路归并排序，接口类似 qsort，多了一个 swap 参数用作临时工作区，
   要求其大小不小于 base 的大小，归并排序是稳定的排序算法 */
extern void BkMsort(void *base, unsigned long n, unsigned long size,
   int (*f)(const void*, const void*), void *swap);

/* 比较字符串 s1 的尾部是否是字符串 s2 */
extern int BkStrRCmp(const char *s1, const char *s2);

/* 向抽象单向链表中加入数据 */
extern void BkAddDataToList(BK_LIST *list, void *data);

/* 初始化抽象单向链表，len 是数据项长度 */
extern void BkInitList(BK_LIST *list, int len);

/* 二分法查找，找到返回下标，否则返回 -1。要求数组是从大到小排序的。
   如果有键值相等的将返回第一笔记录的下标 */
extern int BkBinarySearch(void *array, int size, int n, void *data,
   int (*f)(const void*, const void*));

/* ASCII 码与 EBCDIC 码互转 */
extern char BkAtoe(char t);
extern char BkEtoa(char t);
extern void BkConv(int dir, void *inbuf, void *outbuf, int len);

/* 字符是否在字符集合中出现 */
extern int BkCharIsInSet(char c, const char *set);

/* 初始化 SmartArray */
extern void BkSmartArrayInit(BK_SMART_ARRAY *arr, size_t RecSize, int step);

/* 从 SmartArray 取数据*/
extern void *BkSmartArrayLoad(BK_SMART_ARRAY *arr, int subscript);

/* 向 SmartArray 存数据 */
extern void BkSmartArrayStore(BK_SMART_ARRAY *arr, int subscript, void *data);

/* 取 SmartArray 记录笔数 */
extern int BkSmartArrayRecNum(BK_SMART_ARRAY *arr);

/* 销毁 SmartArray */
extern void BkSmartArrayDestory(BK_SMART_ARRAY *arr);

/* 返回文件长度 */
extern off_t BkFileSize(const char *File);

/* 字符串转换成数值 */
extern int BkStrToInt(const char *Str);
extern int BkHexStrToInt(const char *Str);
extern float BkStrToFloat(const char *Str);
extern double BkStrToDouble(const char *Str);
extern long double BkStrToLongDouble(const char *Str);

/* 时间相减 */
time_t BkSubTimeval(const struct timeval *lhs, const struct timeval *rhs);

typedef void BK_SIG_FUNC(int);

extern BK_SIG_FUNC *BkSignal(int SigNo, BK_SIG_FUNC *Func, int InterruptFlag);

#ifdef __cplusplus
}
#endif

#endif
